<?php
/**
 * Created by PhpStorm.
 * User: inhere
 * Date: 2016/03/4
 * Time: 15:30
 */

namespace app\extensions;

use Slim;
use slimExt\base\Collection;

/**
 * Class MderPage
 * @package app\extensions
 *
 * @property-read string $default
 * @property-read string $dataFile
 * @property-read string $outputFile
 * @property string $page
 * @property string $real
 * @property string $suffix
 * @property bool $exists
 * @property bool $rendered
 *
 * @property array $rawAttr
 * @property array $pageAttr
 */
class MderPage extends Collection
{
    protected $data = [
        'default' => 'index',

        'defaultPage' => '',

        // request page relative path
        'raw' => '',

        // page relative path
        'page' => '',

        // page relative path
        'real' => '',

        // page suffix
        'suffix' => '',

        // raw data file
        'dataFile' => '',

        // rendered static file
        'outputFile' => '',

        'exists' => true,

        // Have already rendered static file
        'rendered' => true,


        // raw data file own settings
        'rawAttr' => [],
    ];

    /**
     * @var array|mixed
     */
    protected $sources = [];

    /**
     * @var array|mixed
     */
    protected $outputs = [];

    /**
     * page belongs to which item.
     * items config see '@src/config/config.yml' tag `items`
     * @var MderItem
     */
    protected $item;

    const DEFAULT_FILE = 'index';
    const DEFAULT_OUTPUT_SUFFIX = '.html';
    const DEFAULT_SOURCE_SUFFIX = '.md';

    /**
     * @param string $page
     */
    public function __construct($page='')
    {
        $this->prepare($page);

        parent::__construct();
    }

    /**
     * @param $page
     * @return $this
     */
    protected function prepare($page)
    {
        if ($page) {
            // clear space
            $page = '/' . trim($page, '/ ');

            if( $suffix = get_extension($page) ) {
                $this->set('suffix', $suffix)->set('raw', $page);
                $page = substr($page, 0 , - (strlen($suffix)+1));
            }

            $this->set('real', $page)->set('page', $page);

            // find Page Item
            $this->findPageItem($page, $suffix);

            $this->sources = Slim::config()->get('sources');
            $this->outputs = Slim::config()->get('outputs');

            if ( !isset($this->sources['path']) || !isset($this->outputs['path'])) {
                throw new \RuntimeException('Must be config the `sources.path` and `outputs.path`');
            }
        }

        return $this;
    }

    /**
     * @param $page
     * @param $suffix
     */
    public function findPageItem($page, $suffix)
    {
        // find page belongs to which item.
        $copy = trim($page,'/');
        $pos  = strpos($copy,'/');

        // fix: visit `/blog` , is item name, it real access page maybe is `/blog/index.md`.
        if ( !$suffix && $pos === false ) {
            $name = $copy;
        } else {
            $name = $pos>0 ? substr($copy, 0, strpos($copy,'/') ) : '';
        }

        $this->item = new MderItem($name);

        $this->data['defaultPage'] = $this->item->getOption('defaultPage');
        $this->data['default'] = Slim::config()->defaultFile ?: static::DEFAULT_FILE;
        if ( substr($this->data['default'],-3) === '.md' ) {
            $this->data['default'] = substr($this->data['default'], 0, -3);
        }

        if ( $name = $this->item->getName() ) {
            $real = substr($this->data['real'], strlen($name)+1);
            $this->data['real'] = '/' . trim($real, '/');
        }
    }

    public function load($page)
    {
        return $this->prepare($page);
    }

    /**
     * @return bool
     */
    public function findPageFile()
    {
        // 数据源目录中以下划线开始的文件或文件夹不会被读取。
        $itemName = $this->item->getName();
        $pageReal   = str_replace(['/', '\\'], DIR_SEP, $this->get('real'));

        $outputPath = $this->item->getOption('outPath') ? : $this->getOutput('path') . DIR_SEP . $itemName;
        $dataPath = $this->item->getOption('path') ? : $this->getSource('path') . DIR_SEP . $itemName;
        $outputPath = Slim::alias($outputPath);
        $dataPath = Slim::alias($dataPath);

        $this->outputFile = $outputPath . $pageReal . $this->getOutput('suffix', '.html');
        $this->dataFile   = $dataPath . $pageReal . $this->getSource('suffix','.md');

        // Have already rendered static file ?
        // if not exists, find raw data file
        if ( !file_exists($this->outputFile) ) {
            $this->rendered = false;
            $this->exists = file_exists($this->dataFile);

            // if dataFile not exists AND '$dataPath' is dir . find {@see $defaultFile} Page In Subdir
            if ( !$this->exists && is_dir($dataPath) ) {
                $this->findDefaultFileInSubdir($outputPath, $dataPath);
            }
        }

        return $this->exists;
    }

    /**
     * if '$dataPath' is dir, will check '$dataPath/index.md' is exists or not.
     * @param string $outputPath
     * @param string $dataPath
     * @return array|bool
     */
    protected function findDefaultFileInSubdir($outputPath, $dataPath)
    {
        $outSuffix = $this->getOutput('suffix', self::DEFAULT_OUTPUT_SUFFIX);
        $srcSuffix = $this->getSource('suffix', self::DEFAULT_SOURCE_SUFFIX);

        if ( ($default = $this->data['defaultPage']) && trim($this->data['page'],'/') === $this->getItemName() ) {

            if ( substr($default,-3) === self::DEFAULT_SOURCE_SUFFIX ) {
                $default = substr($default, 0, -3);
            }

            $outputFile = Slim::alias($outputPath) . DIR_SEP . $default . $outSuffix;
            $dataFile   = Slim::alias($dataPath) . DIR_SEP . $default . $srcSuffix;

        } elseif ( $default = $this->get('default') ) {
            $outputFile = $outputPath . DIR_SEP . $default . $outSuffix;
            $dataFile   = $dataPath . DIR_SEP . $default . $srcSuffix;
        }

        $this->rendered = $this->exists = true;

        // Have already rendered static file ?
        if ( !file_exists($outputFile) ) {
            $this->rendered = false;

            // find raw data file
            if ( !($this->exists = file_exists($dataFile)) ) {
                return false;
            }
        }

        $this->set('real',  ($this->real === '/' ? '': $this->real ). '/' . $default)
             ->set('dataFile', $dataFile)
             ->set('outputFile', $outputFile);

        return true;
    }

    /**
     * @param $key
     * @param mixed $default
     * @return mixed
     */
    public function getSource($key, $default=null)
    {
        return isset($this->sources[$key]) ? $this->sources[$key] : $default;
    }

    /**
     * @param $key
     * @param mixed $default
     * @return mixed
     */
    public function getOutput($key, $default=null)
    {
        return isset($this->outputs[$key]) ? $this->outputs[$key] : $default;
    }

    public function getSources()
    {
        return $this->sources;
    }

    public function getOutputs()
    {
        return $this->outputs;
    }

    /**
     * @return boolean
     */
    public function isRendered()
    {
        return $this->get('rendered',false);
    }

    /**
     * @return MderItem
     */
    public function getItem()
    {
        return $this->item;
    }

    /**
     * @return string
     */
    public function getItemName()
    {
        return $this->item->getName();
    }

    /**
     * @param bool|false $output
     * @return string
     */
    public function getPageUrl($output=false)
    {
        return '/' . $this->getItemName() . $this->get('real') . (
            $output ? $this->getOutput('suffix', '.html') : $this->getSource('suffix','.md')
        );
    }

    public static function checkPath($path)
    {
        $regex = '/^\/[a-zA-Z][\w-]{1,12}(?:\/[-.\w]+){0,6}\.md$/';
//        $regex = '/^/[a-zA-Z][\w-]{1,12}(?:/[\w-.]+){0,6}(?:.html|.md)?/';

        return preg_match($regex, $path) === 1;
    }
}